package io.inputstream ;

import java.io.BufferedInputStream ;
import java.io.BufferedOutputStream ;
import java.io.File ;
import java.io.FileInputStream ;
import java.io.FileNotFoundException ;
import java.io.FileOutputStream ;
import java.io.IOException ;
import java.io.InputStream ;
import java.io.PushbackInputStream ;
import java.util.ArrayList ;
import java.util.Arrays ;
import java.util.List ;

/**
 * PushBackInputStram提供了一个回压栈，可以为运行中的流写入数据
 * PushBackInputStream首先读取流顶部缓冲堆栈内的数据，然后在读取底层流内的数据 
 * 当调用unread方法向缓冲区写数据的时候，要注意，缓冲区要提供足够的空间以便接受这些数据，否则抛异常：Push back buffer is full
 * PushBackInputStream的缓冲区大小在创建实例的时候设置new PushBackInputStream(InputStream,bufferSize)。不设置，则默认为1
 * 
 * 在测试PushBackInputStream的时候，发现一个有趣的现象就是：在获得byte流之后，读取流数据位String的时候。
 * long begin = System.nanoTime() ;
        int len = 0 ;
        byte[] cache = new byte[bufferSize] ;
        StringBuilder sb = new StringBuilder() ;
        try {
            while ((len = is.read(cache)) != -1) {
                sb.append(new String(cache, 0, len)) ;
            }
        } catch (IOException e) {
            e.printStackTrace() ;
        }
        long end = System.nanoTime() ;
        System.out.println("all time ：" + (end - begin)) ;
        return sb.toString() ;
 * 1、如果使用一个512字节byte数组接受流内数据的话，转换为String用时648284纳秒
 * 2、如果用一个1字节的byte数组接受流内的数据的话，转换为String用时5431896纳秒；
 * long begin = System.nanoTime() ;
        StringBuilder sb = new StringBuilder() ;
        int data = 0 ;
        try {
            while ((data = is.read()) != -1) {
                char trueData = (char) ((byte) data) ;
                sb.append(trueData) ;
            }
        } catch (IOException e) {
            e.printStackTrace() ;
        }
        long end = System.nanoTime() ;
        System.out.println("all time ：" + (end - begin)) ;
        return sb.toString() ;
 * 3、不使用byte数组来接收数据，直接使用read方法，用时432417纳秒。
 * 难道用缓存时间还更少？
 * 然后换了测试的时候，只测试读取数据，生产字符串的操作没有放到测试时间里面，果然，还是使用缓存的效率更好
 * 512字节缓存用时：375285 ； 1字节缓存用时：809414 ；不用byte数组，直接读取字节数据用时：397522
 * 所以可以看出，费时的是在字符串创建的时候，而且，可能byte越大，创建字符串耗时更多
 * @author 80002165
 * @date 2017年1月13日 上午9:13:20
 */
public class TestPushBackStream {
    private static List<Integer> backdrop = new ArrayList<Integer>() ;
    
    public static void main(String[] args) {
//        tesEncrypte() ;
//        try {
//            byte[] datas = decryptStream(new BufferedInputStream(new FileInputStream(new File("e:/encryptionTxt.txt")))) ;
//            System.out.println(new String(datas)) ;
//        } catch (FileNotFoundException e) {
//            e.printStackTrace();
//        }
        
        testPushBackInputStream(); 
    }
    
    private static void testPushBackInputStream() {
        BufferedInputStream bis = null ;
        PushbackInputStream pbis = null ;
        try {
            bis = new BufferedInputStream(new FileInputStream(new File("e:/testBufferedInputstream.txt"))) ;
            pbis = new PushbackInputStream(bis, 100) ;
            
            byte[] data = { (byte) 'f', (byte) 's', (byte) 'h', (byte) 'l', (byte) 'o', (byte) 'v', (byte) 'e' } ;
            pbis.unread(data) ;
            // 512-648284 ,375285(读完流) ; 1-5431896 809414（读完流）
             System.out.println(readStreamToStr(pbis,512));
            // 432417  397522(读完流)
//            System.out.println(readStreamToStrByOne(pbis)) ;
            
        } catch (FileNotFoundException e) {
            e.printStackTrace() ;
        } catch (IOException e) {
            e.printStackTrace() ;
        } finally {
            try {
                if (bis != null)
                    bis.close() ;
            } catch (IOException e) {
                e.printStackTrace() ;
            }
            try {
                if (pbis != null)
                    pbis.close() ;
            } catch (IOException e) {
                e.printStackTrace() ;
            }
        }
    }
    
    /**
     * 将流中的数据读到缓存
     * 
     * @author 80002165
     * @date 2017年1月13日 上午10:03:01
     * @param is
     * @return
     * @throws IOException 
     */
    private static String readStreamToStr(InputStream is, int bufferSize) throws IOException {
        int len = 0 ;
        byte[] cache = new byte[bufferSize] ;
        StringBuilder sb = new StringBuilder() ;
        byte[] all = new byte[is.available()] ;
        int allLen = 0 ;
        try {
            long begin = System.nanoTime() ;
            while ((len = is.read(cache)) != -1) {
//                sb.append(new String(cache, 0, len)) ;
                for(int i=0 ; i<len; i++){
                    byte dataTemp = cache[i] ;
                    all[allLen + i] = dataTemp  ;
                }
                allLen += len ;
            }
            long end = System.nanoTime() ;
            System.out.println("all time ：" + (end - begin)) ;
        } catch (IOException e) {
            e.printStackTrace() ;
        }
        return new String(all,0,allLen);
    }
    
    /**
     * 一个字节一个字节的读取流中的数据，将最终数据转为字符串
     * 
     * @author 80002165
     * @date 2017年1月13日 上午10:10:54
     * @param is
     * @return
     */
    private static String readStreamToStrByOne(InputStream is) {
        
        StringBuilder sb = new StringBuilder() ;
        int data = 0 ;
        try {
            long begin = System.nanoTime() ;
            while ((data = is.read()) != -1) {
                char trueData = (char) ((byte) data) ;
                sb.append(trueData) ;
            }
            long end = System.nanoTime() ;
            System.out.println("all time ：" + (end - begin)) ;
        } catch (IOException e) {
            e.printStackTrace() ;
        }
        return sb.toString() ;
    }
    
    /**
     * 加密流在对应的位置加上某些干扰数据，在某些位置减少流数据；
     * 加上的数据和减少的数据用一个map记录下来，加上的数据的位置为key用正数，减少的数据位置key为负数 先隔一个字符加一个干扰字节，干扰字节随机
     * 
     * @author 80002165
     * @date 2017年1月13日 上午9:22:46
     */
    public static byte[] encryptionStream(PushbackInputStream pis) {
        int data = 0 ;
        byte[] dataPool = null ;
        int index = 0 ;
        try {
            dataPool = new byte[pis.available()] ;
            
            while ((data = pis.read()) != -1) {
                if(index % 2 == 0){
                    pis.unread("*".getBytes()) ;
                    backdrop.add(index) ;
                }
//                pis.unread(1) ;
                try {
                    dataPool[index++] = (byte) data ;
                } catch (ArrayIndexOutOfBoundsException e) {
                    dataPool = extendArray_btye(dataPool, dataPool.length) ;
                    dataPool[index++] = (byte) data ;
                }
                
            }
        } catch (IOException e) {
            e.printStackTrace() ;
        }
        return Arrays.copyOf(dataPool, index+1) ;
    }
    
    /**
     * 解密流
     * 
     * @author 80002165
     * @date 2017年1月13日 上午11:04:43
     * @param is
     * @return
     */
    public static byte[] decryptStream(InputStream is) {
        int data = 0 ;
        byte[] dataPool = null ;
        int dataIndex = 0 ;
        try {
            dataPool = new byte[is.available()] ;
            int index = 0 ;
            
            while ((data = is.read()) != -1) {
                if(backdrop.contains(index)){
                    dataPool[dataIndex++] = (byte) data ;
                }
               index++ ;
            }
        } catch (Exception e) {
            e.printStackTrace() ;
        }
        return Arrays.copyOf(dataPool, dataIndex + 1) ;
    }
    
    /**
     * 测试加密流数据
     * 
     * @author 80002165
     * @date 2017年1月13日 上午11:05:41
     */
    public static void tesEncrypte() {
        BufferedInputStream bis = null ;
        PushbackInputStream pis = null ;
        BufferedOutputStream bos = null ;
        try {
            bis = new BufferedInputStream(new FileInputStream(new File("e:/testBufferedInputstream.txt"))) ;
            pis = new PushbackInputStream(bis, 1) ;
            byte[] dataPool = encryptionStream(pis) ;
            bos = new BufferedOutputStream(new FileOutputStream("e:/encryptionTxt.txt")) ;
            bos.write(dataPool) ;
        } catch (FileNotFoundException e) {
            e.printStackTrace() ;
        } catch (IOException e) {
            e.printStackTrace() ;
        } finally {
            try {
                if (bos != null)
                    bos.close() ;
            } catch (IOException e) {
                e.printStackTrace() ;
            }
            try {
                if (pis != null)
                    pis.close() ;
            } catch (IOException e) {
                e.printStackTrace() ;
            }
            try {
                if (bis != null)
                    bis.close() ;
            } catch (IOException e) {
                e.printStackTrace() ;
            }
        }
        
    }
    
    /**
     * 扩展数组的长度，在原有的数组长度上，加上扩展的长度
     * 
     * @author 80002165
     * @date 2017年1月13日 上午10:52:45
     * @param originalArr
     *            原数组
     * @param extendLen
     *            扩展的长度
     * @return
     */
    private static <T> T[] extendArray(T[] originalArr, int extendLen) {
        extendLen = extendLen < 0 ? 0 : extendLen ;
        extendLen += originalArr.length ;
        return Arrays.copyOf(originalArr, extendLen) ;
    }
    
    private static byte[] extendArray_btye(byte[] originalArr, int extendLen) {
        extendLen = extendLen < 0 ? 0 : extendLen ;
        extendLen += originalArr.length ;
        return Arrays.copyOf(originalArr, extendLen) ;
    }
}
